using DevExpress.ExpressApp.Blazor.Services;
using EasyXaf.EntityDesigner.BusinessObjects.Entities.EntityProperties;
using EasyXaf.EntityDesigner.Editors.DetailViewEditors.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace EasyXaf.EntityDesigner.Editors.DetailViewEditors.Components;

public partial class PanelComponent : IAsyncDisposable
{
    private string PanelId => $"id-{Panel.Oid}";

    private DotNetObjectReference<PanelComponent> DotNetObjectRef { get; set; }

    private IJSObjectReference JsModule { get; set; }

    [Inject]
    private IJSRuntime JSRuntime { get; set; }

    [Inject]
    private IXafApplicationProvider ApplicationProvider { get; set; }

    [Parameter]
    public bool Root { get; set; }

    [Parameter]
    public string CssClass { get; set; }

    [Parameter]
    public string Style { get; set; }

    [Parameter]
    public Panel Panel { get; set; }

    [JSInvokable]
    public async ValueTask InsertPropertyEditorAsync(int index, string propertyPath)
    {
        var entityProperty = Panel.LayoutDesigner.Entity.FindPropertyByPath(propertyPath);
        if (entityProperty != null)
        {
            var propertyEditor = new PropertyEditor(Panel, Panel.LayoutDesigner)
            {
                EntityProperty = entityProperty,
                EntityPropertyPath = propertyPath
            };

            index = Math.Max(Math.Min(Panel.Components.Count, index), 0);

            Panel.Components.Insert(index, propertyEditor);
            Panel.LayoutDesigner.RaiseLayoutChanged();

            await InvokeAsync(StateHasChanged);
        }
    }

    [JSInvokable]
    public async ValueTask InsertComponentAsync(int index, string componentName)
    {
        await InsertComponentAsync(componentName, async component =>
        {
            index = Math.Max(Math.Min(Panel.Components.Count, index), 0);

            Panel.Components.Insert(index, component);
            Panel.LayoutDesigner.RaiseLayoutChanged();

            await InvokeAsync(StateHasChanged);
        });
    }

    [JSInvokable]
    public async ValueTask MoveComponentAsync(int index, string componentId)
    {
        if (Guid.TryParse(componentId, out var id))
        {
            var component = Panel.LayoutDesigner.FindComponent(id);
            if (component != null)
            {
                if (component.Parent is Panel panel)
                {
                    panel.Components.Remove(component);
                }

                index = Math.Max(Math.Min(Panel.Components.Count, index), 0);

                component.Parent = Panel;
                Panel.Components.Insert(index, component);
                Panel.LayoutDesigner.RaiseLayoutChanged();

                await InvokeAsync(StateHasChanged);
            }
        }
    }

    [JSInvokable]
    public async ValueTask SortComponentAsync(int oldIndex, int newIndex, string componentId)
    {
        if (Guid.TryParse(componentId, out var id))
        {
            var component = Panel.LayoutDesigner.FindComponent(id);
            if (component != null)
            {
                oldIndex = Panel.Components.IndexOf(component);
                newIndex = Math.Max(Math.Min(Panel.Components.Count, newIndex - 1), 0);

                Panel.Components.Move(oldIndex, newIndex);
                Panel.LayoutDesigner.RaiseLayoutChanged();

                await InvokeAsync(StateHasChanged);
            }
        }
    }

    private async ValueTask InsertComponentAsync(string componentName, Func<Component, ValueTask> insertAction)
    {
        Component component = null;

        switch (componentName)
        {
            case "HorizontalPanel":
                component = new Panel(Panel, Panel.LayoutDesigner)
                {
                    LayoutDirection = LayoutDirection.Horizontal,
                };
                break;
            case "VerticalPanel":
                component = new Panel(Panel, Panel.LayoutDesigner)
                {
                    LayoutDirection = LayoutDirection.Vertical,
                };
                break;
            case "Tabs":
                component = new Tabs(Panel, Panel.LayoutDesigner);
                break;
            default:
                {
                    var application = ApplicationProvider.GetApplication();
                    var objectSpace = application.CreateObjectSpace(typeof(EntityProperty));
                    var entityProperty = objectSpace.CreateObject<EntityProperty>();
                    entityProperty.Entity = objectSpace.GetObject(Panel.LayoutDesigner.Entity);
                    entityProperty.PropertyType = Enum.GetValues<PropertyType>().Where(t => t.ToString() == componentName).FirstOrDefault();
                    var view = application.CreateDetailView(objectSpace, entityProperty);
                    application.ShowViewStrategy.ShowViewInPopupWindow(view, async () =>
                    {
                        objectSpace.CommitChanges();
                        await insertAction(new PropertyEditor(Panel, Panel.LayoutDesigner)
                        {
                            EntityProperty = entityProperty,
                            EntityPropertyPath = entityProperty.Name
                        });
                    });
                }
                break;
        }

        if (component != null)
        {
            await insertAction(component);
        }
    }

    private string GetComponentStyle(Component component)
    {
        var style = string.Empty;
        if (Panel.LayoutDirection == LayoutDirection.Horizontal)
        {
            style = $"flex: {component.Size ?? 1};";
        }
        return style;
    }

    private void OnDirectionChanged()
    {
        if (Panel.LayoutDirection == LayoutDirection.Horizontal)
        {
            Panel.LayoutDirection = LayoutDirection.Vertical;
        }
        else
        {
            Panel.LayoutDirection = LayoutDirection.Horizontal;
        }
    }

    private void OnSetting()
    {
        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(Panel));
        objectSpace.Committed += (s, e) =>
        {
            Panel.LayoutDesigner.RaiseLayoutChanged();
            InvokeAsync(StateHasChanged);
        };
        var view = application.CreateDetailView(objectSpace, Panel, true);
        application.ShowViewStrategy.ShowViewInPopupWindow(view);
    }

    private void OnDelete()
    {
        if (Panel.Parent is Panel panel)
        {
            panel.Components.Remove(Panel);
            panel.LayoutDesigner.RaiseLayoutChanged();
        }
    }

    private string GetPanelClass()
    {
        var panelClass = $"panel {CssClass}";

        if (Panel.LayoutDirection == LayoutDirection.Horizontal)
        {
            panelClass += " horizontal";
        }
        else
        {
            panelClass += " vertical";
        }

        if (!Panel.Components.Any())
        {
            panelClass += " empty";
        }

        if (Root)
        {
            panelClass += " root";
        }

        return panelClass;
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();

        Panel.Components.CollectionChanged += async (sender, e) =>
        {
            await InvokeAsync(StateHasChanged);
        };
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            var jsModuleFile = "/Editors/DetailViewEditors/Components/PanelComponent.razor.js";
            JsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", jsModuleFile);
            DotNetObjectRef = DotNetObjectReference.Create(this);
            await JsModule.InvokeVoidAsync("init", PanelId, DotNetObjectRef);
        }
    }

    public async ValueTask DisposeAsync()
    {
        try
        {
            if (JsModule != null)
            {
                await JsModule.InvokeVoidAsync("dispose", DotNetObjectRef);
            }
        }
        catch (JSDisconnectedException)
        {
        }

        if (JsModule != null)
        {
            await JsModule.DisposeAsync();
            JsModule = null;
        }

        DotNetObjectRef?.Dispose();
        DotNetObjectRef = null;

        GC.SuppressFinalize(this);
    }
}